--[[
	桌子的共用功能
	1. 有2个人在座位上，桌子在一个非初状态卡住1分钟，说明这个桌子已经坏了，直接踢人关闭桌子
	2. 有人都无人入座, 超过5分钟没开始游戏的桌子，可以删除了
--]]
local cjson = require("cjson")
local table_insert = table.insert
local math_floor = math.floor

local M = class("DeskBase")

function M:ctor()
	self.desk_id = g.desk_id
	self.desk_type = 0

	self.capacity = 100 --桌子可容下最大人数
	self.seat_count = 6 --可游戏的最大人数 g.room_cfg.seat_count
	self.limit_time = nil --限制时间
	self.locked = false --是否锁住
	self.closed = false --关闭桌子
	self.create_time = g.time() --创建时间
	self.start_time = 0 --开始时间
	self.lid = 0 --牌局id
	self.remove_robots = false --是否删除机器人
	self.old_player_num = 0 --桌子老玩家数
	self.stage_logs = {} --每个状态的下注情况
	self.round_start_time = 0 --每局开始时间
	self.total_fee = 0 --每局的税
	self.round_num = 0 --桌子开始后第几局
	self.players = {} --参与游戏的玩家,确实玩过游戏的玩家有记录
	self.players_over = {} --玩到一局结束的玩家

end

-- remote 不在同一个gameserver的玩家坐到一桌
--给指定玩家发消息
function M:send(user, cmd, msg_name, ...)
	if user:getDisconnect() then
		return
	end
	if not user:getAgent() then return end --没有agent无法发消息

	if user:getRemote() then
		if user:isRobot() then
			if msg_name == "TS_2C_leaveBc" or msg_name == "TS_2C_gameoverBc" or msg_name == "TS_2C_standupBc" then
				return g.message:sendNode(user:getRemote(), user:getAgent(), cmd, user:getUid(), msg_name, ...)
			end
			return
		end
		return g.message:sendNode(user:getRemote(), user:getAgent(), cmd, msg_name, ...)
	else
		--g.log:debug("user:isRobot()",user:isRobot(), user:getUid())
		if user:isRobot() then
			--return g.send(".bot.service", cmd, msg_name, ...)
			if g.robot then
				local func = g.robot.tb_callback[msg_name]
				if func then
					func(...)
				end
			end
			return
		end
		--g.log:debug("texas:send#######", cmd, msg_name, ...)
		return g.send(user:getAgent(), cmd, msg_name, ...)
	end
end

function M:call(user, cmd, ...)
	if user:getDisconnect() then
		return
	end
	if not user:getAgent() then
		g.log:deskWarn(user:getUid(), "call agent is nil ", user:getAgent(), ...)
		return
	end --没有agent无法发消息
	if user:getRemote() then
		if user:isRobot() then
			return g.message:callNode(user:getRemote(), user:getAgent(), cmd, user:getUid(), ...)
			--return
		end
		return g.message:callNode(user:getRemote(), user:getAgent(), cmd, ...)
	else
		--print("desk call#######", cmd, ...)
		return g.call(user:getAgent(), cmd, ...)
	end
end

function M:callUser(user, ...)
	if not user then return end
	g.log:deskDebug(user:getUid(), "callUser", ...)
	if not user:getDisconnect() then
		return self:call(user, "send_user", ...)
	else
		g.log:deskInfo(user:getUid(), "callUser but user is disconnect", user:getDisconnect())
	end
end

function M:sendUser(user, ...)
	if not user then return end
	g.log:deskDebug(user:getUid(), "sendUser", ...)
	if not user:getDisconnect() then
		self:send(user, "send_user", ...)
	else
		g.log:deskInfo(user:getUid(), "sendUser but user is disconnect", user:getDisconnect())
	end
end

--给桌子所有玩家广播消息
function M:sendUsers(...)
	--g.log:deskDebug(0, "sendUsers", ...)
	local user_list = g.user_mgr:getUsers()
	for _, v in pairs(user_list) do
		--判断是否在线
		if not v:getDisconnect() then
			self:send(v, "send_user", ...)
		end
	end
end

--给别的玩家发消息
function M:sendOther(uid, ...)
	--g.log:deskDebug(uid, "sendOther", ...)
	local user_list = g.user_mgr:getOthers(uid)
	for _, v in pairs(user_list) do
		--print("1111111111", v:getDisconnect(), v:getUid())
		--判断是否在线
		if not v:getDisconnect() then
			self:send(v, "send_user", ...)
		end
	end
end

--给无座玩家发消息
function M:sendToNoSeat(...)
	g.log:deskDebug(0, "sendToNoSeat", ...)
	local users = g.user_mgr:getUsers()
	for _,v in pairs(users) do
		--判断是否在线
		--判断是否有座位
		 if not v:isSeating() then
			 self:send(v, "send_user", ...)
		 end
	end
end

--清除离线的玩家
function M:removeDisconnect()
	local user_list = g.user_mgr:getUserList()	
	for user_id, user in pairs(user_list) do
		if user:getDisconnect() then
			g.user_mgr:removeUser(user_id)
			print("踢走掉线#######",user_id)
		end
	end
end

--当前状态
function M:getState()
	return self.fsm:get()
end

--状态对应的数字
function M:getStateNum()
	local state = self:getState()
	for k, v in pairs(self.stateTable) do 
		if state == v[1] then 
			return k
		end
	end
	return 0
end

--是否比赛场
function M:isMatchRoom()
	if g.rtype and g.rtype == g.const.room_type.match then
		return true
	end
end

--是否普通场
function M:isNormalRoom()
	if g.rtype and g.rtype == g.const.room_type.normal then
		return true
	end
end


--踢出玩家
function M:kickUser(user)
	local uid = user:getUid()
	local seat_id = user:getSeatId()
	g.log:deskDebug(uid, "kickUser")
	--如果在游戏中弃牌
	if user:isGaming() then
		if self.cur_oper_seat_id == seat_id then
			self.oper_timer:deltimer(self.oper_timer_id)
		end
		self:betting(seat_id, -1) --如果在游戏中弃牌
	end
	--站起
	if user:isSeating() then
		g.user_mgr:standUp(uid)
		self:sendUsers("TS_2C_standupBc", {
			mid = uid,
			tid = g.desk_id,
			seatid = seat_id,
		})
	end
	--返还到游戏帐户中
	self:backChip(user)
	self:sendUser(user, "TS_2C_leaveBc", {
		mid = uid,
		tid = g.desk_id,
		seatid = seat_id,
	})
	--删除
	g.user_mgr:removeUser(uid)
	if user:isGaming() then
		if self.cur_oper_seat_id == seat_id then
			self:playerOper()
			return
		end
	end
	--只剩下一个人直接结束
	local play_num = g.user_mgr:getPlayingUserNum()
	if play_num > 0 and play_num < g.desk.start_play_limit then
		if self:getStageNum() < self.state_num[self.s_stage_over] then
			self:changeStage(self.s_stage_over)
		end
	end
end

function M:kickUsers()
	local users = g.user_mgr:getUsers()
	for _, user in pairs(users) do
		--站起
		local uid = user:getUid()
		local sid = user:getSeatId()
		user:addChip(user:getSeatBet())
		self:sendUser(user, "TS_2C_standupBc", {
			mid = uid,
			tid = g.tid,
			seatid = sid,
		})
		--返还到游戏帐户中
		self:backChip(user)
		self:sendUser(user, "TS_2C_leaveBc", {
			mid = uid,
			tid = g.tid,
			seatid = sid,
		})
		--删除
		g.user_mgr:removeUser(uid)
	end
end

--关闭桌子
function M:onClose()
	self.closed = true
	g.timeout(1, function()
		--print("1111onClose111111111111111")
		--让游戏结束
		--返回下注
		local users = g.user_mgr:getPlayingUser()
		for _, user in pairs(users) do
			local bet = user:getSeatBet()
			user:addChip(bet)
			user:setSeatBet(0)
		end
		--print("1111onClose1111111111111112")
		self:kickUsers()
	end)


	g.timeout(4, function()
		g.exit()
	end)
	--print("1111onClose1111111111111113")
	return true
end

--金币返回给玩家账户
function M:backChip(user)
	--返还到游戏帐户中
	local chip = user:getChip()
	local uid = user:getUid()
	if self:isMatchRoom() then
		return
	end
	--g.dp:delTableUserMoney(g.tid, uid)
	if chip <= 0 then
		return
	end
	user:backChip()
end

--牌局id
function M:createRoundId()
	--1658375784827976
	self.lid = g.getMsec()
end

--返回桌子当前状态
function M:getStage()
	return self.fsm:get()
end

function M:getLid()
	return self.lid
end

--输赢流水
function M:sendTableLogs()
	--if not self:hasPlayer() then
	--	return
	--end
	--if g.user_mgr:getPlayerNum() == 0 then
	--	return
	--end
	local users = self:getPlayers()
	for _, user in pairs(users) do
		local uid = user:getUid()
		local sid = user:getSeatId()
		local is_win = 0
		local win = user:getWinChip()
		if win > 0 then is_win = 1 end
		--记录输赢流水日志
		local over_data = {
			lid = self.lid,-- 牌局id
			mid = uid, --玩家id
			rid = g.rid, --房间id
			seatid = sid, --座位id
			win = win, --输赢值
			chipin = user:getSeatBet(), --下注额度
			cards = cjson.encode(user:getCards()), --手牌
			fee = user:getFee(), --税
			seat_chips = user:getChip(), --桌子上金币
			user_chips = user:getGold(), --总金币
			is_win = is_win, --0输 1赢
			is_vpip = user:getVPIP(), --是否入局
			pfr = user:getPFR(), --翻牌: 1 / 加注翻牌 : 2
			wsd = user:getWSD(), --摊牌后 1赢 0输
			wtsd = user:getWTSD(), --是否摊牌
			fold = user:getFold(), --弃牌阶段
			tid = g.tid,
			sb = self.sb,
			bb = self.bb,
			created = g.time(),
			is_match = self:isMatchRoom(),
			is_robot = user:isRobot(),
			cmd = "texas_logs_table_chips",
		}
		--g.sendLogs("logs_table_chips", over_data)
		g.dp:setLogsDeskChip(over_data)
		if not self:isMatchRoom() then
			user:setTexasOver()
		end
	end
	--桌子详细操作记录
	self:sendTableLogsDetail()
end

--牌局id
function M:getRoundId()
	return self.lid
end

--打印一下牌看
function M:printCards(user)
	local user_cards = user:getCards()
	local function getVal(a)
		local val = a & 0x0F
		local tb = {[14]="A", [10]="T", [11]="J", [12]="Q", [13]="K"}
		local str = tb[val] or val
		return ""..str
	end
	local function getColor(a)
		return (a & 0xF0)/16 + 1
	end
	local colors = {"♠", "♥", "♣", "♦",}
	local str = ""
	for _, v in pairs(user_cards) do
		str = str..getVal(v).. colors[getColor(v)] .. " "
	end
	local str1 = ""
	for _, v in pairs(self.common_cards) do
		str1 = str1..getVal(v).. colors[getColor(v)] .. " "
	end
	--g.log:deskInfo(user:getUid(), user:getPersonality(), str, str1, g.rule:getType(user:getRank()))
	if self:hasPlayer() then
		g.log:deskWarn(user:getUid(), user:getPersonality(), str, str1, g.rule:getType(user:getRank()))
	end
end

--删除所有机器人
function M:removeRobots()
	local users = g.user_mgr:getSeatUsers()
	for _, user in pairs(users) do
		if user:isRobot() and not user:isGaming() or user:isGiveup() then
			self:kickUser(user)
		end
	end
end

--设置历史最好牌
function M:sendBestCards()
	local users = g.user_mgr:getPlayingUser()
	for _, user in pairs(users) do
		--if user:getRank() < user.record_rank then
		--	user.record_rank = user:getRank()
			local win = 0
			if user:getWinChip() > 0 then
				win = 1
			end
			local best_cards = user:getBestCards()
			--user.record_cards = best_cards
			local msg = {
				best_cards = cjson.encode(best_cards),
				rank = user:getRank(),
				mid = user:getUid(),
				info = cjson.encode({
					time = g.time(),
					tid = g.tid,
					lid = self.lid,
					win = win,
				}),
			}
			self:send(user, "update_best_cards", msg)
			--msg.best_cards = cjson.encode(best_cards)
			--g.dp:updateBestCards(msg)
		--end
	end
end

--对玩家输赢处理
function M:addChip(seat_win)
	local users = self:getPlayers()
	for _, user in pairs(users) do
		local sid = user:getSeatId()
		local win_chip = seat_win[sid] or 0
		local win = win_chip - user:getSeatBet() --本局赢得金币
		local min_fee = math_floor(g.room_cfg.min_fee)
		if win > 0 then
			if win <= min_fee then
				user:setWinChip(win)
			else
				--小于100的不收税
				local fee = math_floor(win * g.room_cfg.fee_rate/100)
				if fee < 1 then
					fee = 0
				else
					--fee = math_floor(fee / 100) * 100
				end
				if fee >= g.room_cfg.max_fee  then
					fee = math_floor(g.room_cfg.max_fee)
				end
				win_chip = win_chip - fee
				win = win - fee
				user:setFee(fee)
				user:setWinChip(win)
				self:addFee(fee)
			end
		else
			user:setWinChip(win)
		end
		if win_chip > 0 then
			user:addChip(win_chip)
		end

	end

	--g.log:info("addChip22222222", seat_win)
end

--桌子上都是新玩家
function M:isNewRound()
	local users = g.user_mgr:getSeatUsers()
	for _, user in pairs(users) do
		if user:getRound() ~= 0 then
			return false
		end
	end
	return true
end

--老玩家
function M:getOldPlayerNum()
	local num = 0
	local users = g.user_mgr:getSeatUsers()
	for _, user in pairs(users) do
		if user:getRound() > 0 then
			num = num + 1
		end
	end
	return num
end

--牌局详细记录
function M:sendTableLogsDetail()
	local uLogs, ul = {}, {}
	local users = self:getPlayers()
	local all_win_chip = 0
	for _, v in pairs(users) do
		ul = {}
		local win_chip = v:getWinChip()
		ul["mid"] = v:getUid()
		ul["hands"] = v:getCards()
		ul["rank"] = v:getRank()
		ul["kind"] = v:getKind()
		ul["seat_chips"] = v:getChip()
		ul["ab_win_chips"] = win_chip --
		ul["fee_val"] = v:getFee()
		ul["show_hands"] = v:getShowCards()
		ul["seatid"] = v:getSeatId()
		ul["chipin"] = v:getSeatBet()
		ul["pot_indexs"] = v:getWinPot() or {}
		ul["chips"] = v:getChip()
		ul["mix_win"] = 0
		ul["mix_rate"] = 0
		table_insert(uLogs, ul)
		if win_chip > all_win_chip then
			all_win_chip = win_chip
		end
	end
	local common_cards = self.common_cards
	if not next(common_cards) then
		common_cards = {0}
	end
	local game_type = 0
	if self:isMatchRoom() then
		game_type = 1
	end
	local detail = {
		sb = self.sb,
		bb = self.bb,
		game_type = game_type,
	} --操作详情
	detail['full_cards'] = common_cards
	detail['users'] = uLogs
	detail['logs'] = self.stage_logs --每个状态的下注情况
	detail['pos'] = {
		sb_seatid = self.sb_seat_id,
		bb_seatid = self.bb_seat_id,
		dr_seatid = self.banker_seat_id,
	}
	local ok, str_detail = pcall(cjson.encode, detail)
	if not ok then
		g.log:deskErr(0, "encode detail err:", str_detail)
		return
	end
	local bank_uid = 0
	if self.banker then
		bank_uid = self.banker:getUid()
	end
	local data = {
		detail = str_detail,
		fee = self:getFee(),
		created = self.round_start_time,
		rid = g.rid,
		curr = self.round_num,
		tid = g.tid,
		ended = g.time(),
		lid = self.lid,

		win_chip = all_win_chip,
		bb = self.bb,

		cmd = "texas_logs_table_detail",
	}
	--g.sendLogs("logs_table_detail", data)
	g.dp:setLogsDeskDetail(data)
end

--状态日志
function M:addStageLogs()
	local seats = {}
	local users = g.user_mgr:getSeatUsers()
	for k, user in pairs(users) do
		table_insert(seats, {
			seatid = k,
			kind = user:getKind(),
		})
	end
	local stage = self:getStage()
	local pots = {}
	if self.pots[1] == 0 then
		pots[1] = self.total_bet
	else
		pots = self.pots
	end
	self.stage_logs[stage] = {
		seats = seats,
		pots = pots,
		betinfo = {},
	}
end

function M:addBetLogs(user)
	local stage = self:getStage()
	if not self.stage_logs[stage] or not self.stage_logs[stage]["betinfo"] then return end
	local sendData = {}
	sendData["mid"]         = user:getUid()
	sendData["seatid"]      = user:getSeatId()
	sendData["seat_chips"]  = user:getChip()
	sendData["bet"]         = user:getStageBet()
	sendData["action"]      = user:getAction()
	sendData["chipin"]      = user:getCurBet()
	table_insert(self.stage_logs[stage]["betinfo"], sendData)
end

function M:addFee(fee)
	self.total_fee = self.total_fee + fee
end

function M:setFee(fee)
	self.total_fee = fee
end

function M:getFee()
	return self.total_fee
end

--取盲注
function M:getBind()
	if not self.sb or not self.bb then
		if self:isMatchRoom() then
			self.sb = math.floor(self.blind_cfg.small_blind)
			self.bb = self.sb * 2
		else
			self.sb = math.floor(g.room_cfg.small_blind or 0)
			self.bb = math.floor(g.room_cfg.big_blind or 0) --大盲注
		end
	end
	return self.sb, self.bb
end

function M:addPlayer(player)
	table_insert(self.players, player)
end
--参与游戏的玩家
function M:getPlayers()
	return self.players
end
function M:getPlayersNum()
	return #self.players
end

--增加牌局数
function M:sendAddRound()
	for _, user in pairs(self.players) do
		self:send(user, "add_game_round", {is_match = self:isMatchRoom()})
	end
end

--当前局是否有玩家在参与
function M:hasPlayer()
	for k, v in pairs(self.players) do
		if v:isPlayer() then
			return true
		end
	end
	return false
end




--分池计算
function M:calcPots()
	self.pots = {}
	self.pot_seats = {}
	local total_score = {} --各位置下注金额
	for i = 1, self.seat_count do
		table_insert(self.pots, 0)
		table_insert(self.pot_seats, {})
		table_insert(total_score, 0)
	end
	local count = 0 --分池id
	local users_num = table.size(self.seat_bet)
	for sid, bet in pairs(self.seat_bet) do
		if bet>0 then
			total_score[sid] = bet
		end
	end
	while (true) do
		local vec_score = {}
		local alliner_bet = nil
		for sid, bet in pairs(total_score) do
			if bet>0 then
				local user = g.user_mgr:getSeatUser(sid)
				if user and user:isGaming() then
					if user:getAction() == user.s_bet_allin or count > 0 then
						--产生分池
						table_insert(vec_score, bet)
					end
				end
			end
		end
		table.sort(vec_score, function(a, b) return a < b  end) --小到大
		alliner_bet = vec_score[1]
		--有分池
		if(alliner_bet)then
			for sid, bet in pairs(total_score) do
				if bet > 0 then
					local user = g.user_mgr:getSeatUser(sid)
					if not user or not user:isGaming() or (user:getStatus() == user.s_status_giveup and user:getAction() ~= user.s_bet_allin) then --已下注但提前离桌了
						local potbet = total_score[sid]
						if potbet > alliner_bet then
							potbet = alliner_bet
						end
						self.pots[count+1] = self.pots[count+1] + potbet
						total_score[sid] = total_score[sid] - potbet
					elseif(bet >= alliner_bet )then
						self.pots[count+1] = self.pots[count+1] + alliner_bet
						total_score[sid] = total_score[sid] - alliner_bet
						self.pot_seats[count+1][sid] = true
					end
				end
			end
		end
		if(count >= users_num or self.pots[count+1]==0)then
			break;
		end
		count = count+1
	end
	--g.log:deskDebug(0, "calcPots count:", count)
	g.log:deskDebug(0, "calcPots pots:", tostring(self.pots))
	g.log:deskDebug(0, "calcPots self.pot_seats:", tostring(self.pot_seats))
	return count
end


--开牌
function M:pushShowcards(users)
	local cards = {}
	for _, user in pairs(users) do
		local sid = user:getSeatId()
		local card = {seatid = sid, cards=user:getClientCards(), from = 0,}
		table_insert(cards, card)
		self.user_show_cards[sid] = nil --标记显示过了
		user:setIsShowed(true)
	end
	if not next(cards) then return end
	local data = {
		tid = g.desk_id,
		cards = cards,
	}
	self:sendUsers("TS_2C_showcardsBc", data)
end

--结束时亮牌
function M:pushShowcards2()
	if not next(self.user_show_cards) then
		return
	end
	local cards = {}
	for sid, card in pairs(self.user_show_cards) do
		local user = g.user_mgr:getSeatUser(sid)
		if user and not user:getIsShowed() then
			local scard = {seatid = sid, cards=card, from = 1,}
			table_insert(cards, scard)
			self.user_show_cards[sid] = {show = true}
		end
	end
	if not next(cards) then return end
	self.user_show_cards = {}
	local data = {
		tid = g.desk_id,
		cards = cards,
	}
	self:sendUsers("TS_2C_showcardsBc", data)
end

--玩家特殊位置
function M:getSeatName(user)
	local seat = {
		sb = self.sb_seat_id,
		bb = self.bb_seat_id,
		co = self.co_seat_id, --枪口位
		bk = self.banker_seat_id,
	}
	local sid = user:getSeatId()
	for k, v in pairs(seat) do
		if sid == v then
			return k
		end
	end
	return "other"
end



return M